package com.swpu.service_user.service.impl;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import cn.hutool.core.codec.Base64;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.swpu.commonutils.HttpClientUtils;
import com.swpu.commonutils.JwtUtils;
import com.swpu.commonutils.R;
import com.swpu.service_remind.entity.DrinkRemind;
import com.swpu.service_remind.entity.MedicineRemind;
import com.swpu.service_remind.entity.SitRemind;
import com.swpu.service_remind.service.DrinkRemindService;
import com.swpu.service_remind.service.MedicineRemindService;
import com.swpu.service_remind.service.SitRemindService;
import com.swpu.service_user.component.RSAComponent;
import com.swpu.service_user.entity.User;
import com.swpu.service_user.entity.UserInfo;
import com.swpu.service_user.entity.vo.UserAuthor;
import com.swpu.service_user.mapper.UserMapper;
import com.swpu.service_user.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.swpu.service_weight.entity.WeightPlan;
import com.swpu.service_weight.service.WeightPlanService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.security.spec.AlgorithmParameterSpec;
import java.time.Period;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author caicai
 * @since 2024-01-24
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Value("${wx.appid}")
    private String appid;
    @Value("${wx.appsecret}")
    private String appsecret;
    @Autowired
    private WeightPlanService weightPlanService;
    @Autowired
    private DrinkRemindService drinkRemindService;
    @Autowired
    private SitRemindService sitRemindService;
    @Autowired
    private MedicineRemindService medicineRemindService;

    //    获取微信用户登录openId
    @Override
    public String getOpenIdAndSessionKey(UserAuthor userAuthor) throws Exception {
        String res = HttpClientUtils.get("https://api.weixin.qq.com/sns/jscode2session?appid=" + this.appid + "&secret=" + this.appsecret + "&js_code=" + userAuthor.getCode() + "&grant_type=authorization_code");
        return res;
    }

    @Override
    public R login(UserAuthor userAuthor) throws Exception {
        if (userAuthor.getCode().equals("")) {
            return R.error();
        }
        String openIdAndSessionKey = this.getOpenIdAndSessionKey(userAuthor);      //请求微信开放平台获取微信用户信息
        JSONObject jsonObject = JSON.parseObject(openIdAndSessionKey);
        String openid = (String) jsonObject.get("openid");
        if (openid == null) return R.error().message("openid无效或不存在");
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<User>();
        queryWrapper.eq(User::getOpenid, openid);
        User user = this.getOne(queryWrapper);      //验证用户是否已经注册
        if (user != null) {       //已注册则返回token
            String jwtToken = JwtUtils.getJwtToken(user.getOpenid(), user.getNickname());
            return R.ok().data("token", jwtToken).data("userinfo", user).data("javaPublicKey",RSAComponent.getRsaPublicKey());
        } else {        //未注册则新增用户
            User newUser = new User();           //解密微信用户数据
            String userinfoStr = this.wxDecrypt(userAuthor.getEncryptedData(), openIdAndSessionKey, userAuthor.getIv());
            JSONObject userinfo = JSONObject.parseObject(userinfoStr);
            newUser.setOpenid(openid);
            newUser.setGender(userinfo.getInteger("gender"));
            newUser.setNickname(userinfo.getString("nickName"));
            newUser.setAvatarUrl(userinfo.getString("avatarUrl"));   //新增用户
            boolean isSave = this.save(newUser);
            if (!isSave) return R.error().message("新增用户失败");
            else {
                String jwtToken = JwtUtils.getJwtToken(openid, newUser.getNickname());
                return R.ok().data("token", jwtToken).data("javaPublicKey",RSAComponent.getRsaPublicKey());
            }
        }

    }

    @Override
    public User updateUserStepCount(User user, String openid) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getOpenid, openid);
        User userInfo = this.getOne(queryWrapper);
        userInfo.setStepTarget(user.getStepTarget());
        this.update(userInfo, null);
        return userInfo;
    }

    @Override
    public User getUserInfo(String openid) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getOpenid, openid);
        User user = this.getOne(queryWrapper);
        return user;
    }

    @Override
    public void updateUserInfo(String openid, User user) {
        this.updateById(user);
    }

    //返回用户卡路里
    @Override
    public Map getCaloriesRecommend(String openid) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getOpenid, openid);
        User user = this.getOne(queryWrapper);
        LambdaQueryWrapper<WeightPlan> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(WeightPlan::getUserId,openid);
        WeightPlan weightPlan = weightPlanService.getOne(wrapper);
        HashMap<String, Object> map = new HashMap<>();
        if(weightPlan==null){
           map.put("tdee",0);
           return map;
        }
        double BMR = 0;
        double TDEE=0;
        if (user.getGender() == 1) {
            BMR=66+13.7*user.getWeight()+5*user.getHeight()-6.8*DateUtil.ageOfNow(user.getBirthday());
        }else BMR=655+9.5*user.getWeight()+1.8*user.getHeight()-4.7*DateUtil.ageOfNow(user.getBirthday());
        BMR*=weightPlan.getActiveLevel();
        Double weight = user.getWeight();
        Double weightTarget = weightPlan.getWeightTarget();
        Date startDate = weightPlan.getStartDate();
        Date targetDate = weightPlan.getTargetDate();
        long betweenDays = DateUtil.between(startDate, targetDate, DateUnit.DAY);
     double diffCalories=  Math.abs(weight-weightTarget)*7700/betweenDays;
     if(weight>weightTarget){
         TDEE=BMR-diffCalories;
     }else TDEE=BMR+diffCalories;
        map.put("tdee",TDEE);
        return map;
    }

    @Override
    public Map getUserRemindInfo(String openid) {
        HashMap<String,Object> map = new HashMap<>();
        LambdaQueryWrapper<DrinkRemind> drinkWrapper = new LambdaQueryWrapper<>();
        drinkWrapper.eq(DrinkRemind::getUserId,openid);
        DrinkRemind drinkRemind = drinkRemindService.getOne(drinkWrapper);
        LambdaQueryWrapper<SitRemind> sitWrapper = new LambdaQueryWrapper<>();
        sitWrapper.eq(SitRemind::getUserId,openid);
        SitRemind sitRemind = sitRemindService.getOne(sitWrapper);
        LambdaQueryWrapper<MedicineRemind> medicineWrapper = new LambdaQueryWrapper<>();
        sitWrapper.eq(SitRemind::getUserId,openid);
        List<MedicineRemind> medicineReminds = medicineRemindService.list(medicineWrapper);
        map.put("drinkRemindDuration",drinkRemind.getDuration());
        map.put("sitRemindDuration",sitRemind.getDuration());
        map.put("medicineRemindPieces",medicineReminds.size());
        return map;
    }

    //解密用户数据
    public String wxDecrypt(String encryptedData, String sessionKeyAndOpenId, String vi) throws Exception {
        // 开始解密
        //  String json =  redisTemplate.opsForValue().get(RedisKey.WX_SESSION_ID + sessionId);
        //  log.info("之前存储在redis中的信息："+json);
        //之前存储在redis中的信息：{"session_key":"G59Evf\/Em54X6WsFsrpA1g==","openid":"o2ttv5L2yufc4-VoSPhTyUnToY60"}
        JSONObject jsonObject = JSON.parseObject(sessionKeyAndOpenId);
        String sessionKey = (String) jsonObject.get("session_key");
        byte[] encData = Base64.decode(encryptedData);
        byte[] iv = Base64.decode(vi);
        byte[] key = Base64.decode(sessionKey);
        AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
        return new String(cipher.doFinal(encData), "UTF-8");
    }
}
